if (!dojo._hasResource["dojo.dnd.Selector"]) { //_hasResource checks added by build. Do not use _hasResource directly in your code.
    dojo._hasResource["dojo.dnd.Selector"] = true;
    dojo.provide("dojo.dnd.Selector");

    dojo.require("dojo.dnd.common");
    dojo.require("dojo.dnd.Container");

    /*
     Container item states:
     ""			- an item is not selected
     "Selected"	- an item is selected
     "Anchor"	- an item is selected, and is an anchor for a "shift" selection
     */

    dojo.declare("dojo.dnd.Selector", dojo.dnd.Container, {
        // summary: a Selector object, which knows how to select its children

        constructor: function(node, params) {
            // summary: a constructor of the Selector
            // node: Node: node or node's id to build the selector on
            // params: Object: a dict of parameters, recognized parameters are:
            //	singular: Boolean: allows selection of only one element, if true
            //	the rest of parameters are passed to the container
            if (!params) {
                params = {};
            }
            this.singular = params.singular;
            // class-specific variables
            this.selection = {};
            this.anchor = null;
            this.simpleSelection = false;
            // set up events
            this.events.push(
                    dojo.connect(this.node, "onmousedown", this, "onMouseDown"),
                    dojo.connect(this.node, "onmouseup", this, "onMouseUp"));
        },

        // object attributes (for markup)
        singular: false,    // is singular property

        // methods
        getSelectedNodes: function() {
            // summary: returns a list (an array) of selected nodes
            var t = new dojo.NodeList();
            var e = dojo.dnd._empty;
            for (var i in this.selection) {
                if (i in e) {
                    continue;
                }
                t.push(dojo.byId(i));
            }
            return t;	// Array
        },
        selectNone: function() {
            // summary: unselects all items
            return this._removeSelection()._removeAnchor();	// self
        },
        selectAll: function() {
            // summary: selects all items
            this.forInItems(function(data, id) {
                this._addItemClass(dojo.byId(id), "Selected");
                this.selection[id] = 1;
            }, this);
            return this._removeAnchor();	// self
        },
        deleteSelectedNodes: function() {
            // summary: deletes all selected items
            var e = dojo.dnd._empty;
            for (var i in this.selection) {
                if (i in e) {
                    continue;
                }
                var n = dojo.byId(i);
                this.delItem(i);
                dojo._destroyElement(n);
            }
            this.anchor = null;
            this.selection = {};
            return this;	// self
        },
        insertNodes: function(addSelected, data, before, anchor) {
            // summary: inserts new data items (see Container's insertNodes method for details)
            // addSelected: Boolean: all new nodes will be added to selected items, if true, no selection change otherwise
            // data: Array: a list of data items, which should be processed by the creator function
            // before: Boolean: insert before the anchor, if true, and after the anchor otherwise
            // anchor: Node: the anchor node to be used as a point of insertion
            var oldCreator = this._normalizedCreator;
            this._normalizedCreator = function(item, hint) {
                var t = oldCreator.call(this, item, hint);
                if (addSelected) {
                    if (!this.anchor) {
                        this.anchor = t.node;
                        this._removeItemClass(t.node, "Selected");
                        this._addItemClass(this.anchor, "Anchor");
                    } else if (this.anchor != t.node) {
                        this._removeItemClass(t.node, "Anchor");
                        this._addItemClass(t.node, "Selected");
                    }
                    this.selection[t.node.id] = 1;
                } else {
                    this._removeItemClass(t.node, "Selected");
                    this._removeItemClass(t.node, "Anchor");
                }
                return t;
            };
            dojo.dnd.Selector.superclass.insertNodes.call(this, data, before, anchor);
            this._normalizedCreator = oldCreator;
            return this;	// self
        },
        destroy: function() {
            // summary: prepares the object to be garbage-collected
            dojo.dnd.Selector.superclass.destroy.call(this);
            this.selection = this.anchor = null;
        },

        // markup methods
        markupFactory: function(params, node) {
            params._skipStartup = true;
            return new dojo.dnd.Selector(node, params);
        },

        // mouse events
        onMouseDown: function(e) {
            // summary: event processor for onmousedown
            // e: Event: mouse event
            if (!this.current) {
                return;
            }
            if (!this.singular && !dojo.dnd.getCopyKeyState(e) && !e.shiftKey &&
                (this.current.id in this.selection)) {
                this.simpleSelection = true;
                dojo.stopEvent(e);
                return;
            }
            if (!this.singular && e.shiftKey) {
                if (!dojo.dnd.getCopyKeyState(e)) {
                    this._removeSelection();
                }
                var c = this.getAllNodes();
                if (c.length) {
                    if (!this.anchor) {
                        this.anchor = c[0];
                        this._addItemClass(this.anchor, "Anchor");
                    }
                    this.selection[this.anchor.id] = 1;
                    if (this.anchor != this.current) {
                        var i = 0;
                        for (; i < c.length; ++i) {
                            var node = c[i];
                            if (node == this.anchor || node == this.current) {
                                break;
                            }
                        }
                        for (++i; i < c.length; ++i) {
                            var node = c[i];
                            if (node == this.anchor || node == this.current) {
                                break;
                            }
                            this._addItemClass(node, "Selected");
                            this.selection[node.id] = 1;
                        }
                        this._addItemClass(this.current, "Selected");
                        this.selection[this.current.id] = 1;
                    }
                }
            } else {
                if (this.singular) {
                    if (this.anchor == this.current) {
                        if (dojo.dnd.getCopyKeyState(e)) {
                            this.selectNone();
                        }
                    } else {
                        this.selectNone();
                        this.anchor = this.current;
                        this._addItemClass(this.anchor, "Anchor");
                        this.selection[this.current.id] = 1;
                    }
                } else {
                    if (dojo.dnd.getCopyKeyState(e)) {
                        if (this.anchor == this.current) {
                            delete this.selection[this.anchor.id];
                            this._removeAnchor();
                        } else {
                            if (this.current.id in this.selection) {
                                this._removeItemClass(this.current, "Selected");
                                delete this.selection[this.current.id];
                            } else {
                                if (this.anchor) {
                                    this._removeItemClass(this.anchor, "Anchor");
                                    this._addItemClass(this.anchor, "Selected");
                                }
                                this.anchor = this.current;
                                this._addItemClass(this.current, "Anchor");
                                this.selection[this.current.id] = 1;
                            }
                        }
                    } else {
                        if (!(this.current.id in this.selection)) {
                            this.selectNone();
                            this.anchor = this.current;
                            this._addItemClass(this.current, "Anchor");
                            this.selection[this.current.id] = 1;
                        }
                    }
                }
            }
            dojo.stopEvent(e);
        },
        onMouseUp: function(e) {
            // summary: event processor for onmouseup
            // e: Event: mouse event
            if (!this.simpleSelection) {
                return;
            }
            this.simpleSelection = false;
            this.selectNone();
            if (this.current) {
                this.anchor = this.current;
                this._addItemClass(this.anchor, "Anchor");
                this.selection[this.current.id] = 1;
            }
        },
        onMouseMove: function(e) {
            // summary: event processor for onmousemove
            // e: Event: mouse event
            this.simpleSelection = false;
        },

        // utilities
        onOverEvent: function() {
            // summary: this function is called once, when mouse is over our container
            this.onmousemoveEvent = dojo.connect(this.node, "onmousemove", this, "onMouseMove");
        },
        onOutEvent: function() {
            // summary: this function is called once, when mouse is out of our container
            dojo.disconnect(this.onmousemoveEvent);
            delete this.onmousemoveEvent;
        },
        _removeSelection: function() {
            // summary: unselects all items
            var e = dojo.dnd._empty;
            for (var i in this.selection) {
                if (i in e) {
                    continue;
                }
                var node = dojo.byId(i);
                if (node) {
                    this._removeItemClass(node, "Selected");
                }
            }
            this.selection = {};
            return this;	// self
        },
        _removeAnchor: function() {
            if (this.anchor) {
                this._removeItemClass(this.anchor, "Anchor");
                this.anchor = null;
            }
            return this;	// self
        }
    });

}
